package utils

import (
	"TaskAssignmentSystem/pkg/timehandler"
	"bytes"
	"crypto/md5"
	"crypto/rand"
	"encoding/base64"
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/mintance/go-uniqid"
	"gopkg.in/mgo.v2/bson"
	"io"
	"math/big"
	mRand "math/rand"
	"net/url"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"
)

// CopyProperties 结构体深度拷贝
func CopyProperties(src, dst interface{}) (err error) {
	if src == nil {
		return nil
	}
	if dst == nil {
		return errors.New("dst is nil")
	}
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("CopyStructProperties error, %v", e)
		}
	}()
	if !IsPtr(dst) {
		return errors.New("type must be a struct pointer")
	}
	b, err := json.Marshal(src)
	if err != nil {
		return err
	}
	decoder := json.NewDecoder(bytes.NewReader(b))
	decoder.UseNumber()
	return decoder.Decode(&dst)
}

// IsPtr 指针结构体或切片
func IsPtr(param interface{}) bool {
	if param == nil {
		return false
	}
	t := reflect.TypeOf(param)
	if t.Kind() != reflect.Ptr {
		return false
	}
	return t.Elem().Kind() == reflect.Struct || t.Elem().Kind() == reflect.Slice
}

func StructToMap(i interface{}) (values url.Values) {
	values = url.Values{}
	iVal := reflect.ValueOf(i).Elem()
	typ := iVal.Type()
	for i := 0; i < iVal.NumField(); i++ {
		f := iVal.Field(i)
		var v string
		switch f.Interface().(type) {
		case int, int8, int16, int32, int64:
			v = strconv.FormatInt(f.Int(), 10)
		case uint, uint8, uint16, uint32, uint64:
			v = strconv.FormatUint(f.Uint(), 10)
		case float32:
			v = strconv.FormatFloat(f.Float(), 'f', 4, 32)
		case float64:
			v = strconv.FormatFloat(f.Float(), 'f', 4, 64)
		case []byte:
			v = string(f.Bytes())
		case string:
			v = f.String()
		}
		values.Set(typ.Field(i).Name, v)
	}
	return
}

// 识别手机号码

func IsMobile(mobile string) bool {
	result, _ := regexp.MatchString(`^(13[0-9]|14[0-9]|15[0-9]|166|17[0-9]|18[0-9]|19[1|3|5|8|9])\d{8}$`, mobile)
	if result {
		return true
	} else {
		return false
	}
}

type ObjectId string

// 生成10位id

func SetCode(id string) ObjectId {
	var b [5]byte
	mRand.Seed(time.Now().UnixNano())
	//i2 := rand.Uint32()
	// Timestamp, 4 bytes, big endian
	binary.BigEndian.PutUint32(b[:], uint32(time.Now().UnixNano()))
	// Increment, 3 bytes, big endian
	//i := atomic.AddUint32(&objectIdCounter, i2)
	i := bson.ObjectIdHex(id).Counter()
	b[0] = byte(i >> 16)
	b[4] = byte(i)
	return ObjectId(b[:])
}

func (id ObjectId) String() string {
	return fmt.Sprintf(`ObjectIdHex("%x")`, string(id))
}

func (id ObjectId) Hex() string {
	return hex.EncodeToString([]byte(id))
}

// 短信验证码

func GenValidateCode(width int) string {
	numeric := [10]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
	r := len(numeric)
	mRand.Seed(time.Now().UnixNano())

	var sb strings.Builder
	for i := 0; i < width; i++ {
		fmt.Fprintf(&sb, "%d", numeric[mRand.Intn(r)])
	}
	return sb.String()
}

func MD5(v string) string {
	d := []byte(Md5Key + v)
	m := md5.New()
	m.Write(d)
	return hex.EncodeToString(m.Sum(nil))
}

//base64加密

func Base64Encode(src string) string {
	//coder := base64.NewEncoding(Md5Key)
	//base64.StdEncoding.EncodeToString([]byte(data))
	return base64.StdEncoding.EncodeToString([]byte(src))
}

//base64解密

func Base64Decode(src string) (string, error) {
	decodeString, err := base64.StdEncoding.DecodeString(string(src))
	return string(decodeString), err
}

func UnixToTime(e string) (datetime time.Time, err error) {
	data, err := strconv.ParseInt(e, 10, 64)
	datetime = time.Unix(data/1000, 0)
	return
}

func CreateRandomNumber(len int) string {
	var numbers = []byte{1, 2, 3, 4, 5, 7, 8, 9}
	var container string
	length := bytes.NewReader(numbers).Len()

	for i := 1; i <= len; i++ {
		random, err := rand.Int(rand.Reader, big.NewInt(int64(length)))
		if err != nil {

		}
		container += fmt.Sprintf("%d", numbers[random.Int64()])
	}
	return container
}

func GenerateSpu() string {
	spuRune := uniqid.New(uniqid.Params{})[7:13]
	var str string
	for _, i2 := range spuRune {
		str += fmt.Sprintf("%d", i2)
	}

	return str[0:8] + CreateRandomNumber(5)
}

//生成redis过期事件处理的key

func GenerateRedisKey(topicId uint, value string) (res string) {
	res += fmt.Sprintf("task_topic@%d_%s", topicId, value)
	return
}

func UrlEncodeParam(urlStr string) string {
	if urlStr == "" {
		return urlStr
	}
	if parseRequestURI, err := url.ParseRequestURI(urlStr); err != nil {
		return urlStr
	} else {
		if strings.Contains(parseRequestURI.String(), ",") {
			return strings.ReplaceAll(parseRequestURI.String(), ",", "%2C")
		} else {
			return parseRequestURI.String()
		}
	}
}

func UrlFileNameEncode(urlStr string) string {
	urlStr = strings.ReplaceAll(urlStr, "+", "%2B")
	unescape, err := url.QueryUnescape(urlStr)
	if err != nil {
		unescape = urlStr
	}
	split := strings.Split(unescape, "/")
	if len(split) > 7 {
		return strings.Join(split[:7], "/") + "/" + strings.ReplaceAll(url.QueryEscape(strings.Join(split[7:], "/")), "+", "%20")
	} else {
		return urlStr
	}
}

// 随机数字验证码
func RandNumCodeToString(max int) string {
	var table = [...]byte{'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}
	b := make([]byte, max)
	n, err := io.ReadAtLeast(rand.Reader, b, max)
	if n != max {
		panic(err)
	}
	for i := 0; i < len(b); i++ {
		b[i] = table[int(b[i])%len(table)]
	}
	return string(b)
}

// 判断字符串是否为空或者仅包含空格
func StringIsEmpty(param string) bool {
	if len(param) == 0 {
		return true
	}
	param = strings.ReplaceAll(param, " ", "")
	if len(param) == 0 {
		return true
	}
	return false
}

// 字符串判断非空
func StringIsNotEmpty(param string) bool {
	return !StringIsEmpty(param)
}

// 字符串转 int
func StringToInt(param string) (res int, err error) {
	return strconv.Atoi(param)
}

// 字符串转 uint
func StringToUint(param string) (res uint, err error) {
	intValue, err := strconv.Atoi(param)
	if err != nil {
		return res, errors.New("参数异常")
	}
	return uint(intValue), nil
}

// 字符串转 float64
func StringToFloat64(param string) (float64, error) {
	return strconv.ParseFloat(param, 64)
}

// float64 转字符串
func Float64ToString(param float64) string {
	return strconv.FormatFloat(param, 'f', -1, 32)
}

// int转字符串
func IntToString(param int) string {
	return strconv.Itoa(param)
}

// int64转字符串
func Int64ToString(param int64) string {
	return strconv.FormatInt(param, 10)
}

// uint转字符串
func UintToString(param uint) string {
	return strconv.Itoa(int(param))
}
func GetWeekStartEnd(date string) (string, string) {
	now, _ := time.Parse(timehandler.FormatLayoutDate, date)
	weekStartDate := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local).AddDate(0, 0, -(int(now.Weekday()) - 1))
	weekStartEnd := time.Date(weekStartDate.Year(), weekStartDate.Month(), weekStartDate.Day(), 0, 0, 0, 0, time.Local).AddDate(0, 0, 6)
	return weekStartDate.Format(timehandler.FormatLayoutDate), weekStartEnd.Format(timehandler.FormatLayoutDate)
}
func GetMonthStartEnd(date string) (string, string) {
	now, _ := time.Parse(timehandler.FormatLayoutDate, date)
	monthStartDate := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local).AddDate(0, 0, -(now.Day() - 1))
	days := getYearMonthToday(now.Year(), int(now.Month()))
	monthEndDate := time.Date(monthStartDate.Year(), monthStartDate.Month(), monthStartDate.Day(), 0, 0, 0, 0, time.Local).AddDate(0, 0, days-1)
	return monthStartDate.Format(timehandler.FormatLayoutDate), monthEndDate.Format(timehandler.FormatLayoutDate)
}
func getYearMonthToday(year int, month int) int {
	//有31天的月份
	day31 := map[int]bool{
		1:  true,
		3:  true,
		5:  true,
		7:  true,
		8:  true,
		10: true,
		12: true,
	}
	if day31[month] == true {
		return 31
	}
	// 有30天的月份
	day30 := map[int]bool{
		4:  true,
		6:  true,
		9:  true,
		11: true,
	}
	if day30[month] == true {
		return 30
	}
	//计算平年还是闰年
	if (year%4 == 0 && year%100 != 0) || year%400 == 0 {
		// 得出二月天数
		return 29
	}
	// 得出平年二月天数
	return 28
}
